package com.dc.project.config;


import com.dc.base.common.constants.SessionKeyConstants;
import com.dc.common.util.RandomUtil;
import org.flywaydb.core.internal.util.FileCopyUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.MappedInterceptor;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * swagger文档配置
 *
 * @author Enzo
 * @date 2020-5-21
 */
@Profile({"dev", "test"})
@EnableSwagger2
@Configuration
public class SwaggerConfiguration {
    @Value("${swagger.basic.username:admin}")
    private String username;
    @Value("${swagger.basic.password:admin}")
    private String password;

    /**
     * 定义全局header参数
     *
     * @return java.util.List<springfox.documentation.service.Parameter>
     * @author Enzo
     * @date 2020-12-23
     **/
    private List<Parameter> getParams() {
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder submitKey = new ParameterBuilder();
        submitKey.name("submitKey").defaultValue("").description("表单防重票据").modelRef(new ModelRef("string"))
                .parameterType("header").required(false).build();
        pars.add(submitKey.build());
        return pars;
    }

    @Bean
    public Docket createRestApi() {
        return getDocker("active", "com.dc.project.core.controller");
    }

    @Bean
    public Docket createRestApiByDynamic() {
        return getDocker("动态表单", "com.dc.base.dynamic");
    }

    @Bean
    public Docket createRestApiByBase() {
        return getDocker("基础框架", "com.dc.base.controller");
    }

    private Docket getDocker(String groupName, String basePackage) {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName(groupName).select()
                .apis(RequestHandlerSelectors.basePackage(basePackage))
                .paths(PathSelectors.any())
                .build()
//                .securityContexts(securityContexts())
                .securitySchemes(securitySchemes())
                .globalOperationParameters(getParams())
                .apiInfo(apiInfo());
    }


    /**
     * 全局认证参数key
     *
     * @param
     * @return java.util.List<springfox.documentation.service.ApiKey>
     * @author Enzo
     * @date 2020-7-7
     **/
    private List<ApiKey> securitySchemes() {
        List<ApiKey> apiKeyList = new ArrayList<>();
        apiKeyList.add(new ApiKey("authToken", "authToken", "header"));
        return apiKeyList;
    }

    /**
     * 全局认证参数默认配置
     *
     * @param
     * @return java.util.List<springfox.documentation.service.SecurityReference>
     * @author Enzo
     * @date 2020-7-7
     **/
    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("authToken", authorizationScopes));
        return securityReferences;
    }

    /**
     * 全局认证参数内容
     *
     * @param
     * @return java.util.List<springfox.documentation.spi.service.contexts.SecurityContext>
     * @author Enzo
     * @date 2020-7-7
     **/
    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(PathSelectors.regex("^(?!login).*$"))
                        .build());
        return securityContexts;
    }


    /**
     * 设置文档描述信息
     *
     * @return springfox.documentation.service.ApiInfo
     * @author Enzo
     * @date 2020-5-21
     **/
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("API接口文档")
                .description("Rest API接口")
                .version("1.0").build();
    }

    @Bean
    @ConditionalOnProperty(name = "swagger.basic.enable", havingValue = "true")
    public MappedInterceptor getMappedInterceptor() {
        return new MappedInterceptor(new String[]{"/swagger-ui.html", "/v2/api-docs", "/webjars/**"}, new SwaggerInterceptor(username, password));
    }

    class SwaggerInterceptor implements HandlerInterceptor {
        private String username;
        private String password;

        public SwaggerInterceptor(String username, String password) {
            this.username = username;
            this.password = password;
        }

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String authorization = request.getHeader("Authorization");
            boolean isAuthSuccess = httpBasicAuth(authorization);
            if (!isAuthSuccess) {
                response.setCharacterEncoding("utf-8");
                response.setStatus(401);
                response.setHeader("WWW-authenticate", "Basic realm=\"Realm\"");
                try (PrintWriter writer = response.getWriter()) {
                    writer.print("Forbidden, unauthorized user");
                }
            }
            return isAuthSuccess;
        }

        public boolean httpBasicAuth(String authorization) throws IOException {
            if (authorization != null && authorization.split(" ").length == 2) {
                String userAndPass = new String(Base64.getDecoder().decode((authorization.split(" ")[1])));
                String username = userAndPass.split(":").length == 2 ? userAndPass.split(":")[0] : null;
                String password = userAndPass.split(":").length == 2 ? userAndPass.split(":")[1] : null;
                if (this.username.equals(username) && this.password.equals(password)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            String uri = request.getRequestURI();
            AntPathMatcher pathMatcher = new AntPathMatcher();
            if (!pathMatcher.match("/swagger-ui.html", uri) && !pathMatcher.match("/webjars/**", uri)) {
                response.setStatus(404);
                return;
            }
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath:/META-INF/resources" + uri);
            if (resources != null && resources.length > 0) {
                FileCopyUtils.copy(resources[0].getInputStream(), response.getOutputStream());
            } else {
                response.setStatus(404);
            }
        }
    }

}
